package com.api.rc.service.impl;

import com.alibaba.fastjson.JSON;
import com.api.base.utils.PageInfoUtils;
import com.api.core.service.AbstractService;
import com.api.rc.dao.HostMapper;
import com.api.rc.dao.RoomMapper;
import com.api.rc.enums.HostRuleKey;
import com.api.rc.enums.HostStatus;
import com.api.rc.enums.RoomFee;
import com.api.rc.form.HostQuestionForm;
import com.api.rc.form.HostTestForm;
import com.api.rc.model.Host;
import com.api.rc.model.HostRule;
import com.api.rc.model.Room;
import com.api.rc.service.HostService;
import com.api.rc.service.RuleService;
import com.api.rc.vo.HostLevelRuleVo;
import com.api.rc.vo.HostOverviewVo;
import com.api.rc.vo.HostVo;
import com.api.rc.vo.RoomVo;
import com.api.user.service.UserInfoService;
import com.api.user.vo.UserInfoVo;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.base.Preconditions;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author RenQiang
 * @date 2021/7/5
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class HostServiceImpl extends AbstractService<Host> implements HostService {
    /**
     * 成为主持人需要的答题正确率
     */
    private static final BigDecimal TEST_PROGRESS_TO_BE_HOST = new BigDecimal("0.8");

    private final HostMapper hostMapper;
    private final RuleService ruleService;
    private final UserInfoService userInfoService;
    private final RoomMapper roomMapper;

    public HostServiceImpl(HostMapper hostMapper, RuleService ruleService, UserInfoService userInfoService, RoomMapper roomMapper) {
        this.hostMapper = hostMapper;
        this.ruleService = ruleService;
        this.userInfoService = userInfoService;
        this.roomMapper = roomMapper;
    }

    @Override
    public void batchUpdateQuestions(List<HostQuestionForm> hostQuestionForms) {
        Preconditions.checkArgument(!CollectionUtils.isEmpty(hostQuestionForms), "参数hostQuestionForms不能为空");

        hostQuestionForms.sort(Comparator.comparing(HostQuestionForm::getOrder));
        for (int i = 0; i < hostQuestionForms.size(); i++) {
            HostQuestionForm questionForm = hostQuestionForms.get(i);
            questionForm.setOrder(i + 1);
        }

        ruleService.saveOrUpdateRule(HostRuleKey.Questions, hostQuestionForms);
    }

    @Override
    public List<HostQuestionForm> getQuestions() {
        HostRule hostRule = ruleService.getHostRuleByKey(HostRuleKey.Questions);
        return Optional.ofNullable(hostRule)
                .map(r -> {
                    List<HostQuestionForm> hostQuestionForms = JSON.parseArray(r.getValue(), HostQuestionForm.class);
                    return hostQuestionForms.stream()
                            .sorted(Comparator.comparing(HostQuestionForm::getOrder))
                            .collect(Collectors.toList());
                }).orElse(Collections.emptyList());
    }

    @Override
    public void updateTutorialVideo(String videoUrl) {
        Preconditions.checkArgument(StringUtils.isNoneBlank(videoUrl), "参数videoUrl不能为空");

        ruleService.saveOrUpdateRule(HostRuleKey.TutorialVideo, videoUrl);
    }

    @Override
    public String getTutorialVideo() {
        HostRule hostRule = ruleService.getHostRuleByKey(HostRuleKey.TutorialVideo);
        return Optional.ofNullable(hostRule)
                .map(r -> JSON.parseObject(r.getValue(), String.class))
                .orElse(null);
    }

    @Override
    public HostOverviewVo test(Long userId, List<HostTestForm> hostTestForms) {
        List<HostQuestionForm> questions = this.getQuestions();
        Map<Integer, String> answerMap = hostTestForms.stream()
                .collect(Collectors.toMap(HostTestForm::getOrder, HostTestForm::getAnswer));
        long rightCount = questions.stream()
                .filter(q -> q.getAnswers().stream()
                        .filter(HostQuestionForm.HostQuestionAnswer::getIsRight)
                        .anyMatch(a -> a.getAnswer().equals(answerMap.get(q.getOrder()))))
                .count();
        BigDecimal testProgress = BigDecimal.valueOf(rightCount)
                .divide(BigDecimal.valueOf(questions.size()), 2, BigDecimal.ROUND_HALF_UP);
        Host host = this.getOrInitHost(userId);
        host.setTestProgress(testProgress);
        host.setUpdateTime(new Date());
        if (testProgress.compareTo(TEST_PROGRESS_TO_BE_HOST) > 0) {
            host.setLevel(0);
        }
        host.setTestScore((int) rightCount);
        hostMapper.updateByPrimaryKey(host);
        return getOverview(userId);
    }

    @Override
    public List<HostLevelRuleVo> getLevelRules() {
        HostRule hostRule = ruleService.getHostRuleByKey(HostRuleKey.Level);
        return JSON.parseArray(hostRule.getValue(), HostLevelRuleVo.class);
    }

    @Override
    public HostOverviewVo getOverview(Long userId) {
        UserInfoVo userInfoVo = userInfoService.getIdVoMap(Collections.singletonList(userId)).get(userId);
        Host hostExample = new Host();
        hostExample.setUserId(userId);
        Host host = hostMapper.selectOne(hostExample);
        return buildHostOverviewVo(host, userInfoVo);
    }

    @Override
    public List<RoomFee> getAvailableRoomFees(Long userId) {
        HostOverviewVo hostOverviewVo = this.getOverview(userId);
        Preconditions.checkArgument(hostOverviewVo.getStatus() == HostStatus.ENABLE, "当前用户没有权限");

        List<HostLevelRuleVo> levelRules = this.getLevelRules();
        return levelRules.stream()
                .filter(r -> r.getLevel().equals(hostOverviewVo.getLevel()))
                .findFirst()
                .map(HostLevelRuleVo::getAvailableRoomFees)
                .orElse(Collections.emptyList());
    }

    @Override
    public Map<Long, HostOverviewVo> batchGetOverview(List<Long> hostIds) {
        String userIdsStr = hostIds.stream()
                .map(Object::toString)
                .collect(Collectors.joining(","));
        List<Host> hosts = hostMapper.selectByIds(userIdsStr);
        List<Long> userIds = hosts.stream()
                .map(Host::getUserId)
                .collect(Collectors.toList());
        Map<Long, UserInfoVo> userIdVoMap = userInfoService.getIdVoMap(userIds);
        return hosts.stream()
                .map(host -> buildHostOverviewVo(host, userIdVoMap.get(host.getUserId())))
                .collect(Collectors.toMap(HostOverviewVo::getHostId, Function.identity()));
    }

    @Override
    public void calcAndUpdateHostLevel(Long hostId) {
        Host host = hostMapper.selectByPrimaryKey(hostId);
        List<HostLevelRuleVo> levelRules = this.getLevelRules();
        int maxLevel = levelRules.stream()
                .mapToInt(HostLevelRuleVo::getLevel)
                .max()
                .orElse(0);
        if (host.getLevel() == maxLevel) {
            log.info("主持人[{}]已到最高等级[{}]", host.getUserId(), maxLevel);
            return;
        }
        Room sample = new Room();
        sample.setHostId(hostId);
        List<Room> hostRooms = roomMapper.select(sample);

        //最小分数对应的游戏场数
        Map<Integer, Integer> hostStarGamesMap = levelRules.stream()
                .flatMap(levelRule -> hostRooms.stream()
                        .filter(room -> room.getHostStar() != null && room.getHostStar() >= levelRule.getMinStar())
                        .map(room -> Pair.of(levelRule.getMinStar(), 1)))
                .collect(Collectors.groupingBy(Pair::getLeft, Collectors.summingInt(Pair::getRight)));

        List<HostLevelRuleVo> orderedLevelRules = levelRules.stream()
                .filter(levelRule -> levelRule.getLevel() >= host.getLevel())
                .sorted(Comparator.comparing(HostLevelRuleVo::getLevel).reversed())
                .collect(Collectors.toList());
        for (HostLevelRuleVo levelRule : orderedLevelRules) {
            Integer targetGames = Optional.ofNullable(hostStarGamesMap.get(levelRule.getMinStar()))
                    .orElse(0);
            if (targetGames >= levelRule.getNeedToHostGames()) {
                host.setLevel(levelRule.getLevel());
                host.setUpdateTime(new Date());
                hostMapper.updateByPrimaryKey(host);
                break;
            }
        }
    }

    @Override
    public void increaseHostGames(Long hostId, Long roomId, BigDecimal earnCoins) {
        int updatedLines = hostMapper.increaseHostGames(hostId, earnCoins);
        Preconditions.checkArgument(updatedLines == 1, "主持人不存在");
    }

    @Override
    public PageInfo<HostVo> listByPage(Map<String, Object> queryParams, Integer page, Integer size) {
        PageHelper.startPage(page, size);
        List<HostVo> hostVos = hostMapper.list(queryParams);
        List<HostQuestionForm> hostQuestions = getQuestions();
        hostVos.forEach(vo -> vo.setTestTotalScore(hostQuestions.size()));
        return new PageInfo<>(hostVos);
    }

    @Override
    public void updateHostAvailable(Long hostId, Boolean available) {
        Host host = hostMapper.selectByPrimaryKey(hostId);
        Preconditions.checkArgument(host != null, "主持人不存在");
        if (!Objects.equals(host.getAvailable(), available)) {
            host.setAvailable(available);
            host.setUpdateTime(new Date());
            hostMapper.updateByPrimaryKey(host);
        }
    }

    private HostOverviewVo buildHostOverviewVo(Host host, UserInfoVo userInfoVo) {
        if (host == null) {
            HostOverviewVo hostOverviewVo = new HostOverviewVo();
            hostOverviewVo.setUserId(userInfoVo.getId());
            hostOverviewVo.setHostName(userInfoVo.getFullName());
            hostOverviewVo.setStatus(HostStatus.NOT_A_HOST);
            return hostOverviewVo;
        } else if (host.getLevel() < 0) {
            HostOverviewVo hostOverviewVo = new HostOverviewVo();
            hostOverviewVo.setUserId(userInfoVo.getId());
            hostOverviewVo.setHostName(userInfoVo.getFullName());
            hostOverviewVo.setStatus(HostStatus.TESTING);
            hostOverviewVo.setTestProgress(host.getTestProgress());
            return hostOverviewVo;
        } else {
            HostOverviewVo hostOverviewVo = new HostOverviewVo();
            hostOverviewVo.setUserId(userInfoVo.getId());
            hostOverviewVo.setHostName(userInfoVo.getFullName());
            hostOverviewVo.setStatus(host.getAvailable() ? HostStatus.ENABLE : HostStatus.BLOCKED);
            hostOverviewVo.setLevel(host.getLevel());
            hostOverviewVo.setCreatedGames(host.getCreatedGames());
            hostOverviewVo.setEarningCoins(host.getEarningCoins());
            hostOverviewVo.setHostId(host.getId());
            hostOverviewVo.setTestProgress(host.getTestProgress());
            return hostOverviewVo;
        }
    }

    private Host getOrInitHost(Long userId) {
        Host hostExample = new Host();
        hostExample.setUserId(userId);
        Host host = hostMapper.selectOne(hostExample);
        if (host == null) {
            host = new Host();
            host.setUserId(userId);
            host.setLevel(-1);
            host.setTestProgress(BigDecimal.ZERO);
            host.setCreatedGames(0);
            host.setEarningCoins(BigDecimal.ZERO);
            Date now = new Date();
            host.setCreateTime(now);
            host.setUpdateTime(now);
            host.setAvailable(true);
            host.setTestScore(0);
            hostMapper.insert(host);
        }
        return host;
    }
}
